﻿#region File and License Information
/*
<File>
	<Copyright>Copyright © 2007, Daniel Vaughan. All rights reserved.</Copyright>
	<License see="prj:///Documentation/License.txt"/>
	<Owner Name="Daniel Vaughan" Email="dbvaughan@gmail.com"/>
	<CreationDate>2009-02-08 17:59:42Z</CreationDate>
	<LastSubmissionDate>$Date: $</LastSubmissionDate>
	<Version>$Revision: $</Version>
</File>
*/
#endregion

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace DanielVaughan.AI.NeuralNetworking
{
	/// <summary>
	/// This class is used to turn an object into the input
	/// for a neural network by examining its public properties.
	/// </summary>
	public class NeuralInputGenerator
	{
		double trueLevel = .99;
		double falseLevel = .01;

		/// <summary>
		/// Gets or sets the value representing <code>true</code> in the neural network.
		/// </summary>
		/// <value>The hi level in the neural network.</value>
		public double TrueLevel
		{
			get
			{
				return trueLevel;
			}
			set
			{
				ArgumentValidator.AssertGreaterThan(value, 0, "value");
				ArgumentValidator.AssertLessThan(value, 1, "value");
				trueLevel = value;
			}
		}

		/// <summary>
		/// Gets or sets the value representing <code>false</code> in the neural network.
		/// </summary>
		/// <value>The false level.</value>
		public double FalseLevel
		{
			get
			{
				return falseLevel;
			}
			set
			{
				ArgumentValidator.AssertGreaterThan(value, 0, "value");
				ArgumentValidator.AssertLessThan(value, 1, "value");
				falseLevel = value;
			}
		}

		readonly SortedList<string, PropertyInfo> propertyInfos = new SortedList<string, PropertyInfo>();
		int propertyCount; /* Number of properties in the instance being represented. */

		Type lastKnownType;
		readonly object lastKnownTypeLock = new object();

		/// <summary>
		/// Generates the input for a neural network.
		/// </summary>
		/// <param name="instance">The object instance that is analysed
		/// in order to produce the result.</param>
		/// <param name="newInstance">if <c>true</c> then this is the first time the neural network 
		/// has been trained in this session.</param>
		/// <returns>The input stimulus for a neural network.</returns>
		public double[] GenerateInput(object instance, bool newInstance)
		{
			ArgumentValidator.AssertNotNull(instance, "instance");
			var clientType = instance.GetType();
			if (lastKnownType == null || lastKnownType != clientType)
			{
				lock(lastKnownTypeLock)
				{
					if (lastKnownType == null || lastKnownType != clientType)
					{
						Initialize(clientType);
					}
				}
			}

			var resultSize = propertyCount + 1;
			var doubles = new double[resultSize];
			/* The first index is reserved as an indicator 
			 * for whether this is a new instance. */
			doubles[0] = newInstance ? trueLevel : falseLevel;

			for (int i = 1; i < resultSize; i++)
			{
				var info = propertyInfos.Values[i - 1];
				if (info.PropertyType == typeof(string))
				{
					var propertyValue = info.GetValue(instance, null);
					doubles[i] = propertyValue != null ? trueLevel : falseLevel;
				}
				else if (info.PropertyType == typeof(bool))
				{
					var propertyValue = info.GetValue(instance, null);
					doubles[i] = (bool)propertyValue ? trueLevel : falseLevel;
				}
				else if (!typeof(ValueType).IsAssignableFrom(info.PropertyType)) 
				{	/* Not a value type. */
					var propertyValue = info.GetValue(instance, null);
					doubles[i] = propertyValue != null ? trueLevel : falseLevel;
				}
			}

			return doubles;
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		void Initialize(Type clientType)
		{
			ArgumentValidator.AssertNotNull(clientType, "clientType");
			var properties = clientType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
			foreach (var propertyInfo in properties)
			{
				if (propertyInfo.PropertyType != typeof(string) && propertyInfo.PropertyType != typeof(bool)
					&& typeof(ValueType).IsAssignableFrom(propertyInfo.PropertyType))
				{
					continue;
				}
				propertyInfos.Add(propertyInfo.Name, propertyInfo);
				propertyCount++;
			}
			lastKnownType = clientType;
		}

//		public double[] GenerateInput(object instance, double[] lastInput)
//		{
//			ArgumentValidator.AssertNotNull(instance, "instance");
//			var clientType = instance.GetType();
//			if (lastKnownType == null || lastKnownType != clientType)
//			{
//				lock (lastKnownTypeLock)
//				{
//					if (lastKnownType == null || lastKnownType != clientType)
//					{
//						Initialize(clientType);
//					}
//				}
//			}
//
//			var resultSize = propertyCount + 1;
//			var doubles = new double[resultSize];
//			/* The first index is reserved as an indicator 
//			 * for whether this is a new instance. */
//			doubles[0] = lastInput == null ? hi : low;
//
//			for (int i = 1; i < resultSize; i++)
//			{
//				var info = propertyInfos.Values[i - 1];
//				if (info.PropertyType == typeof(string))
//				{
//					var propertyValue = info.GetValue(instance, null);
//					doubles[i] = propertyValue != null ? hi : low;
//				}
//				else if (info.PropertyType == typeof(bool))
//				{
//					var propertyValue = info.GetValue(instance, null);
//					doubles[i] = (bool)propertyValue ? hi : low;
//				}
//				else if (!typeof(ValueType).IsAssignableFrom(info.PropertyType))
//				{	/* Not a value type. */
//					var propertyValue = info.GetValue(instance, null);
//					doubles[i] = propertyValue != null ? hi : low;
//				}
//			}
//
//			if (lastInput == null || lastInput.Length != doubles.Length)
//			{
//				return doubles;
//			}
//		
//			var aged = new double[doubles.Length];
//			foreach (var d in aged)
//			{
//				
//			}
//
//			return doubles;
//		}
	}
}
